// 7.6 类的静态成员
/**
 * 有的时候类需要它的一些成员与类本身直接相关，而不是与类的各个对象保持关联。
 * 例如，一个银行账户类可能需要一个数据成员来表示当前的基准利率。在此例中，我们希望利率与类关联，而非与类的每个对象关联。
 * 从实现效率的角度来看，没必要每个对象都存储利率信息。而且更加重要的是，一旦利率浮动，我们希望所有的对象都能使用新值。
 *
 * 声明静态成员我们通过在成员的声明之前加上关键字static使得其与类关联在一起。
 * 必须在类的外部定义和初始化每个静态成员。和其他对象一样，一个静态数据成员只能定义一次
 * 类似于全局变量（参见6.1.1节，第184页），静态数据成员定义在任何函数之外。因此一旦它被定义，就将一直存在于程序的整个生命周期中。
 * 
 * 即使一个常量静态数据成员在类内部被初始化了，通常情况下也应该在类的外部定义一下该成员。
 */

#include <iostream>
#include <vector>
#include "Sales_data.h"
//#include "Screen.h" // 难道是因为Window_mgr被声明成Screen的友元的原因？导致这个源文件同时包含这两个头文件时会导致一些问题？
#include "WindowMgr.h"
using std::cerr, std::clog;
using std::cin, std::cout, std::endl;

// 举个例子，我们定义一个类，用它表示银行的账户记录：
class Account
{
public:
    void calculate() { amount += amount * interestRate; }
    static double rate() { return interestRate; } // 成员函数不用通过作用域运算符就能直接使用静态成员
    static void rate(double); // static关键字则只出现在类内部的声明语句中。

private:
    std::string owner;
    double amount;
    static double interestRate;
    static double initRate();
    // 通常情况下，类的静态成员不应该在类的内部初始化。
    // 然而，我们可以为静态成员提供const整数类型的类内初始值，不过要求静态成员必须是字面值常量类型的constexpr（参见7.5.6节，第267页）。
    static constexpr int period = 30; // period是个常量表达式
    double daily_tbl[period];
};
// 类的静态成员存在于任何对象之外，对象中不包含任何与静态数据成员有关的数据。
// 因此，每个Account对象将包含两个数据成员：owner和amount。只存在一个interestRate对象而且它被所有Account对象共享。
// 类似的，静态成员函数也不与任何对象绑定在一起，它们不包含this指针。
// 作为结果，静态成员函数不能声明成const的，而且我们也不能在static函数体内使用this指针。
// 这一限制既适用于this的显式使用，也对调用非静态成员的隐式使用有效。

// 和其他的成员函数一样，我们既可以在类的内部也可以在类的外部定义静态成员函数。
// static关键字则只出现在类内部的声明语句中。
void Account::rate(double newRate)
{
    interestRate = newRate;
}

// 类似于全局变量（参见6.1.1节，第184页），静态数据成员定义在任何函数之外。因此一旦它被定义，就将一直存在于程序的整个生命周期中。
// 定义并初始化一个静态成员
double Account::interestRate = initRate();

// 即使一个常量静态数据成员在类内部被初始化了，通常情况下也应该在类的外部定义一下该成员。
constexpr int Account::period; // 初始值在类的定义内提供

int main()
{
    double r;
    r = Account::rate(); // 使用作用域运算符访问静态成员

    // 虽然静态成员不属于类的某个对象，但是我们仍然可以使用类的对象、引用或者指针来访问静态成员
    Account ac1;
    Account *ac2 = &ac1;
    // 调用静态成员函数rate的等价形式
    r = ac1.rate();  // 通过Account的对象或引用
    r = ac2->rate(); // 通过指向Account对象的指针


    return 0;
}